using System;
using System.Collections.Generic;
using System.Text;
using MoEngine;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.IO;

namespace MoGameLib
{
    public enum BattleMapLayer
    {
        Board,
        Battlers,
        Effects
    }

    public class BattleMap : IBoard
    {
        protected BattleTileCollection m_Tiles;

        protected int m_Width;
        public int Width
        {
            get { return m_Width; }
            set { m_Width = value; }
        }

        protected int m_Height;
        public int Height
        {
            get { return m_Height; }
            set { m_Height = value; }
        }

        protected SpriteBatch m_SpriteBatch;
        protected Texture2D m_ImageGrid;
        protected Texture2D m_ImageMap;
        protected Texture2D m_TokenOverlayFriendly;
        protected Texture2D m_TokenOverlayEnemy;
        protected Texture2D m_FocusUnderlay;
        protected Texture2D m_FocusedTokenTest;

        protected Scene2D m_Scene;

        public Scene2D Scene
        {
            get { return m_Scene; }
        }

        public BattleMap()
        {
            // create graphics content
            m_SpriteBatch = new SpriteBatch(Core.GlobalDevice);

            // load content
            m_ImageGrid = Core.Content.Load<Texture2D>("Images/GUI/BattleGrid");
            m_ImageMap = Core.Content.Load<Texture2D>("Images/Battle/Maps/KingsRoad");
            m_TokenOverlayFriendly = Core.Content.Load<Texture2D>("Images/GUI/TokenOverlayFriendly");
            m_TokenOverlayEnemy = Core.Content.Load<Texture2D>("Images/GUI/TokenOverlayEnemy");
            m_FocusUnderlay = Core.Content.Load<Texture2D>("Images/GUI/SelectionRing");
            m_FocusedTokenTest = Core.Content.Load<Texture2D>("Images/GUI/TokenOverlayEnemyFocused");
        }

        public void Load(string fileName)
        {
            StreamReader file = new StreamReader(fileName);
            List<string> lines = new List<string>();
            string line;
            while ((line = file.ReadLine()) != null)
            {
                lines.Add(line);
            }

            if (lines.Count > 0)
            {
                int width = m_Width = lines[0].Length / 2;
                int height = m_Height = lines.Count;
                m_Tiles = new BattleTileCollection(width, height);

                int x = 0, y = 0;

                foreach (string l in lines)
                {
                    for (x = 0; x < width; x++)
                    {
                        char ground_char = l[2*x];
                        char overlay_char = l[2*x+1];
                        m_Tiles[x, y] = new BattleTile(this, new Vector2Int(x, y),
                            ground_char == ' ' ? null : MoCore.BattleManager.GroundTypes[ground_char.ToString()], 
                            overlay_char == ' ' ? null : MoCore.BattleManager.OverlayTypes[overlay_char.ToString()]);
                    }
                    y++;
                }

                // setup tiles
                foreach (BattleTile tile in m_Tiles)
                {
                    tile.Setup();
                }
            }

            m_Scene = new Scene2D();
            BattleMapBoard board = new BattleMapBoard(this);
        }

        public void GetAdjacentTiles(int x, int y, out BattleTile[] tiles)
        {
            tiles = new BattleTile[8];
            tiles[0] = m_Tiles[x, y - 1];
            tiles[1] = m_Tiles[x + 1, y - 1];
            tiles[2] = m_Tiles[x + 1, y];
            tiles[3] = m_Tiles[x + 1, y + 1];
            tiles[4] = m_Tiles[x, y + 1];
            tiles[5] = m_Tiles[x - 1, y + 1];
            tiles[6] = m_Tiles[x - 1, y];
            tiles[7] = m_Tiles[x - 1, y - 1];
        }

        private List<Battler> m_Battlers = new List<Battler>();

        public void AddBattler(Battler battler)
        {
            // create a proxy for the creature
            m_Battlers.Add(battler);
            BattleTile tile = GetTile(battler.TileLocaction);

            if (tile != null)
                tile.Battler = battler;
       }

        public BattleTile GetTile(int x, int y)
        {
            return m_Tiles[x, y];
        }

        public BattleTile GetTile(Vector2Int loc)
        {
            return GetTile(loc.X, loc.Y);
        }

        public BattleTile GetTile(Point loc)
        {
            return GetTile(loc.X, loc.Y);
        }

        public Vector2Int ToTileLoc(Vector2 realLoc)
        {
            return new Vector2Int(
                (int)Math.Round(realLoc.X) / MapTileSize.X,
                (int)Math.Round(realLoc.Y) / MapTileSize.Y);
        }

        #region Tracers
        
        public BattleTile[] TraceTiles(Vector2 start, Vector2 end, bool bFirstHit)
        {
            Vector2Int curLoc = ToTileLoc(start);
            Vector2Int endLoc = ToTileLoc(end);

            if (curLoc == endLoc)
                return new BattleTile[0];
            
            return new BattleTile[0];

        }

        #endregion


        #region IBoard Members

        static public readonly Vector2Int MapTileSize = new Vector2Int(80, 80); // new Point(32, 32);
        static readonly Point TokenSize = new Point(72, 72);

        private Point CameraTopLeft = new Point();

        public Vector2Int TileSize
        {
            get { return MapTileSize; }
        }

        public virtual void Update(GameTime gameTime)
        {
            m_Scene.Update(gameTime);
        }

        public virtual void Render(ICamera camera)
        {
            m_Scene.Render(camera as Camera2D);

            // determine drawing range
            //Point leftTop = camera.TopLeft;
            //int left = (int)leftTop.X;
            //int top = (int)leftTop.Y;
            //CameraTopLeft.X = left;
            //CameraTopLeft.Y = top;
            //int minTileX = Math.Max(0, left / (int)TileSize.X);
            //int minTileY = Math.Max(0, top / (int)TileSize.Y);
            //int maxTileX = Math.Min(Width-1, (int)(left + camera.ViewportSize.X) / (int)TileSize.X);
            //int maxTileY = Math.Min(Height-1, (int)(top + camera.ViewportSize.Y) / (int)TileSize.Y);
            //if (minTileX > maxTileX || minTileY > maxTileY)
            //    return;

            //float scale = 1.0f;
            /*


            m_SpriteBatch.Begin(
                SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred,
                SaveStateMode.None, Matrix.CreateScale(scale));

            Rectangle rect;
            rect.Width = TileSize.X;
            rect.Height = TileSize.Y;*/

            /*

            // draw ground
            for (int y = minTileY; y <= maxTileY; y++)
            {
                for (int x = minTileX; x <= maxTileX; x++)
                {
                    BattleTile tile = m_Tiles[x, y];

                    if (tile.Ground != null)
                    {
                        rect.X = (int)Math.Round((double)(-left + TileSize.X * x));
                        rect.Y = (int)Math.Round((double)(-top + TileSize.Y * y));

                        tile.Ground.Draw(m_SpriteBatch, rect);
                    }
                }
            }

            // draw overlay
            for (int y = minTileY; y <= maxTileY; y++)
            {
                for (int x = minTileX; x <= maxTileX; x++)
                {
                    BattleTile tile = m_Tiles[x, y];

                    if (tile.Overlay != null)
                    {
                        rect.X = (int)Math.Round((double)(-left + TileSize.X * x));
                        rect.Y = (int)Math.Round((double)(-top + TileSize.Y * y));

                        tile.Overlay.Draw(m_SpriteBatch, rect);
                    }
                }
            }*/

            // draw map
            /*
            m_SpriteBatch.Draw(m_ImageMap, new Rectangle(-left, -top, (int)Math.Round(m_ImageMap.Width * 0.8f), (int)Math.Round(m_ImageMap.Height * 0.8f)), Color.White);


            // draw grids
            for (int y = minTileY; y <= maxTileY; y++)
            {
                for (int x = minTileX; x <= maxTileX; x++)
                {
                    rect.X = (int)Math.Round((double)(-left + TileSize.X * x));
                    rect.Y = (int)Math.Round((double)(-top + TileSize.Y * y));

                    m_SpriteBatch.Draw(m_ImageGrid, rect, Color.White);
                }
            }
            m_SpriteBatch.End();*/

            // draw battlers
            /*
            m_SpriteBatch.Begin(
                SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred,
                SaveStateMode.None, Matrix.CreateScale(scale));

            foreach (Battler battler in m_Battlers)
            {
                GameVector tileLoc = battler.TileLocaction;
                if (tileLoc.X >= minTileX && tileLoc.X <= maxTileX &&
                    tileLoc.Y >= minTileY && tileLoc.Y <= maxTileY)
                {
                    DrawBattler(battler);
                }
            }
            m_SpriteBatch.End();*/

        }


        private void DrawBattler(Battler battler)
        {
            Rectangle drawRect = new Rectangle(
                (int)Math.Round((double)(-CameraTopLeft.X + TileSize.X * battler.TileLocaction.X + (TileSize.X - TokenSize.X) / 2)),
                (int)Math.Round((double)(-CameraTopLeft.Y + TileSize.Y * battler.TileLocaction.Y + (TileSize.Y - TokenSize.Y) / 2)),
                TokenSize.X,
                TokenSize.Y);

            if (battler == m_FocusedBattler || battler == m_SelectedBattler)
            {
                Rectangle focusRect = drawRect;
                focusRect.Inflate(focusRect.Width/2, focusRect.Height/2);
                m_SpriteBatch.Draw(m_FocusUnderlay, focusRect, Color.White);
            }

            m_SpriteBatch.Draw(battler.Creature.BattleToken, drawRect, Color.White);
            m_SpriteBatch.Draw(battler.Team.IsPlayerControlled() ? m_TokenOverlayFriendly : (/*battler == m_FocusedBattler ? m_FocusedTokenTest : */m_TokenOverlayEnemy), drawRect, Color.White);

        }

        public Point PickTile(ICamera camera, Vector2 cursorPos)
        {
            Point mapPos = new Point(camera.TopLeft.X + (int)cursorPos.X, camera.TopLeft.Y + (int)cursorPos.Y);
            Point tilePos;
            tilePos.X = (int)MathHelper.Clamp((int)mapPos.X / TileSize.X, -1, Width);
            tilePos.Y = (int)MathHelper.Clamp((int)mapPos.Y / TileSize.Y, -1, Height);

            return tilePos;
        }

        #endregion

        public BattleTile GetPickedTile(ICamera camera, Vector2 cursorPos)
        {
            return GetTile(PickTile(camera, cursorPos));
        }

        private Battler m_FocusedBattler;

        public Battler FocusedBattler
        {
            get { return m_FocusedBattler; }
            set { m_FocusedBattler = value; }
        }

        private Battler m_SelectedBattler;

        public Battler SelectedBattler
        {
            get { return m_SelectedBattler; }
            set { m_SelectedBattler = value; }
        }
    }

    public class BattleMapBoard : Graph2D
    {
        BattleMap m_Map;
        protected Texture2D m_ImageGrid;
        protected Texture2D m_ImageMap;

        public BattleMapBoard(BattleMap map)
        {
            m_Map = map;
            m_Map.Scene.Attach(this, (int)BattleMapLayer.Board);
            
            m_ImageGrid = Core.Content.Load<Texture2D>("Images/GUI/BattleGrid");
            m_ImageMap = Core.Content.Load<Texture2D>("Images/Battle/Maps/KingsRoad");
        }

        protected override void Render(Camera2D camera, SpriteBatch spriteBatch)
        {
            base.Render(camera, spriteBatch);
            // determine drawing range
            Point topLeft = camera.TopLeft;
            int minTileX = Math.Max(0, topLeft.X / m_Map.TileSize.X);
            int minTileY = Math.Max(0, topLeft.Y / m_Map.TileSize.Y);
            int maxTileX = Math.Min(m_Map.Width - 1, (topLeft.X + camera.ViewportSize.X) / m_Map.TileSize.X);
            int maxTileY = Math.Min(m_Map.Height - 1, (topLeft.Y + camera.ViewportSize.Y) / m_Map.TileSize.Y);
            if (minTileX > maxTileX || minTileY > maxTileY)
                return;

            float scale = 1.25f;

            Rectangle rect;
            rect.Width = m_Map.TileSize.X;
            rect.Height = m_Map.TileSize.Y;

            Rectangle viewRect = camera.ViewRect;

            //m_SpriteBatch.Draw(m_ImageMap, new Rectangle(-left, -top, (int)Math.Round(m_ImageMap.Width * 0.8f), (int)Math.Round(m_ImageMap.Height * 0.8f)), Color.White);

            spriteBatch.Draw(m_ImageMap, viewRect, new Rectangle((int)Math.Round(viewRect.X * scale), (int)Math.Round(viewRect.Y * scale), (int)Math.Round(viewRect.Width * scale), (int)Math.Round(viewRect.Height * scale)), Color.White);

            // draw grids
            for (int y = minTileY; y <= maxTileY; y++)
            {
                for (int x = minTileX; x <= maxTileX; x++)
                {
                    rect.X = (int)Math.Round((double)(m_Map.TileSize.X * x));
                    rect.Y = (int)Math.Round((double)(m_Map.TileSize.Y * y));

                    spriteBatch.Draw(m_ImageGrid, rect, Color.White);
                }
            }
        }
    }
}
